#include <iostream>


using std::cout;
using std::endl;

#define TraceLn(value) cout << value << endl
#define Trace(value) cout << value

typedef unsigned int UINT;

namespace part1
{

	void part1()
	{
		int integerValue = 90;
		char characterValue = 'f';

		int *integerValuePtr = &integerValue;
		char *charValuePtr = &characterValue;

		Trace("Integer pointer address: ");
		TraceLn(integerValuePtr);

		// directly modifying the value at the pointer's
		// address
		*integerValuePtr = *charValuePtr;

		Trace("Integer pointer referenced value: ");
		TraceLn(*integerValuePtr);

		char newCharacterValue = 90;
		// set the address that charValuePtr is pointing at
		// to the value of newCharacterValue's address
		charValuePtr = &newCharacterValue;

		// I cast to an int pointer so it looks more like an address, I dunno if it holds the same value there or w\e though.
		Trace("Char pointer address: ");
		TraceLn((int*)charValuePtr);

		Trace("Char pointer referenced value: ");
		TraceLn(*charValuePtr);
	}
}


namespace part2
{
	
// the '#' symbol stringizes the variable, giving you a string that matches the
// passed in variable's name. perfect for debug prints.
#define DEBUGTRACELN(variable) cout << "&" #variable ": " << &variable << endl
#define DEBUGTRACELN2(variable) cout << #variable ": " << variable << endl


	void part2()
	{
		int value = 120;

		DEBUGTRACELN(value);
	}

	void part2Test()
	{
	}
}


namespace part3
{
	const float testVar1 = 0.01f;
	const float testVar2 = 0.013f;
	const float testVar3 = 0.014f;

	void part3()
	{
		// Yes, you can print method addresses
		DEBUGTRACELN(part2::part2);
		DEBUGTRACELN(part2::part2Test);

		// the addresses for the constant variables are one after another every 4 bytes
		DEBUGTRACELN(testVar1);
		DEBUGTRACELN(testVar2);
		DEBUGTRACELN(testVar3);

		int unitializedInteger;
		int *intPtr = &unitializedInteger;
		// a bunch of crap is inside of the uninitializedInteger because
		// the OS just allocates memory and doesn't bother to clear it. 
		// It is important to always initialize your variables before using because
		// you never know what is inside of it.
		DEBUGTRACELN(unitializedInteger);
		DEBUGTRACELN2(*intPtr);
	}
}

namespace part4
{

	int LoopToFive()
	{
		UINT i = 5;
		int count = 0;

		__asm
		{
			mov eax, i			;Move the value of 'i' into the eax register
_LOOPHEAD:
			inc count
			dec eax				;decrement the contents of eax
			jz _ENDLOOP			;if the last operation resulted in a zero, jump to the end of the loop
			jmp _LOOPHEAD		;else jump to the beginning of the loop
_ENDLOOP:
			xor eax, eax		;move the contents of the eax register into i
		}

		return count;
	}

	/**
	* Prints the CPU Vendor
	*/
	char* CPUVendor()
	{
		UINT parameter = 0;
		UINT id1 = 0;
		UINT id2 = 0;
		UINT id3 = 0;

		__asm
		{
			mov eax, parameter		;move the parameter variable into the eax register
			CPUID					;Invoke the CPU IDentification instruction, eax = 0 gives us the manufacturer ID string
			mov id1, ebx			;The string is stored in the ebx, edx, and ecx registers in that order.
			mov id2, edx
			mov	id3, ecx
		}

		char* character = new char[12];

		UINT mask = 0xFF;
		character[0] = (id1 & mask);
		character[1] = (id1 & (mask << 8)) >> 8;
		character[2] = (id1 & (mask << 16)) >> 16;
		character[3] = (id1 & (mask << 24)) >> 24;

		character[4] = (id2 & mask);
		character[5] = (id2 & (mask << 8)) >> 8;
		character[6] = (id2 & (mask << 16)) >> 16;
		character[7] = (id2 & (mask << 24)) >> 24;
		
		character[8] = (id3 & mask);
		character[9] = (id3 & (mask << 8)) >> 8;
		character[10] = (id3 & (mask << 16)) >> 16;
		character[11] = (id3 & (mask << 24)) >> 24;

		return character;
	}
	
	void part4()
	{
		char* character = CPUVendor();

		for(int i = 0; i < 12; i++)
			cout << character[i];
		cout << endl;

		delete [] character;
	}
}


void main()
{
	//part1::part1();
	//part2::part2();
	//part3::part3();
	part4::part4();
}